/**
 * \file 3dx.h
 * General Extension to the 3d module.
 *
 *  3D eXtended Module, provides mesh classes 
 *  for loading external meshes and
 *  enables more advanced 3D graphics features.
 *
 * Features:
 *	- XMesh3D class, for loading .x files.
 *  - Per-vertex Tangent computation
 *	- XModel class, for parsing .x files containing
 *	  multiple models and materials
 **
 * Author: Mac Duy Hai, Sean Micklethwaite
 * License... etc.
 */

#ifndef __3DX_H__
#define __3DX_H__

#include <fstream>
#include <string>
#include "glext.h"
#include "wtftypes.h"
#include "3d_material.h"

#define FEATURE_TANGENTS		01

namespace WtfGraphics {
	/**
	 * Base parser - contains core parsing logic
	 **/
	class XParser {
		protected:
			std::ifstream*	mpXFile;
			
		public:
			/// Utility function - skips whitespace in input file
			void skipWs();

			/// Locates the next block and writes its name to the passed string.
			bool locateBlock(std::vector<std::string>& vsBlockTags, bool bPeek = false);

			/// Finds the block beginning
			void startBlock();

			/// Finds the block terminator without touching the block stack.
			void endBlock();

			void ReadMatrix(Transformation3D& trans);

			/// Puts a string back into the file (DANGEROUS)
			void putBack(const std::string& str);
			/// Puts a block header back into the file (DANGEROUS)
			void putBack(const std::vector<std::string>& vsBlockTags);

			inline std::ifstream& xfile() const {
				return *mpXFile;
			};
	};

	/**
	 *	Mesh for loading .x files. Currently supports loading of the first mesh found, loading triangles.
	 */
	class XMesh : public Mesh, private XParser {
		private:
			/// tells the loader to calculate per-vertex tangents and use them in the engine
			bool useTangents;
			Material::tCacheRef mrMaterial;

		public:
			// constructor/destructor
			XMesh(int features = 0);
			XMesh(const char *filename, int scale);
			XMesh(const WtfEngine::StringPool::Entry& sName): Mesh(sName) {
			};

			~XMesh();

			// methods
			void Load(const char *filename, const Transformation3D& trans = Transformation3D::identity());
			/// Read from an already opened XFile
			void Read(std::ifstream* pXFile, const Transformation3D& trans);

			inline const Material::tCacheRef& getMaterial() const {
				return mrMaterial;
			};

			// data manager interface
			static WtfEngine::IDataObject * Load(const WtfEngine::StringPool::Entry& sName);
			DATA_LOADABLE(XMesh, GC_ADDEDSIZE_AUTO(GraphicsData, XMesh));
	};


	/**
	 * Class for loading materials from .x files.
	 **/
	class XMaterial: public DiffuseTextureMaterial, private XParser {
	public:
		XMaterial(const WtfEngine::StringPool::Entry& sName);

		/// Read from .x file
		void Read(const WtfEngine::String& sFile);
		/// Read from an already opened XFile
		void Read(std::ifstream* pXFile);

		// data manager interface
		static WtfEngine::IDataObject * Load(const WtfEngine::StringPool::Entry& sName);
		DATA_LOADABLE(XMaterial, GC_ADDEDSIZE_AUTO(Material, XMaterial));
	};

	class XModel: public Model, private XParser {
		private:
			struct XBlock {
				Transformation3D	Matrix;

				inline XBlock(const Transformation3D& t): Matrix(t) {
				};
			};

			/// This keeps track of how deeply nested we are in the frames.
			std::vector<XBlock>		mBlockStack;

			void pushBlock();
			void popBlock();
			XBlock& getCurrentBlock() {
				return mBlockStack[mBlockStack.size() - 1];
			};

		public:
			XModel(const WtfEngine::StringPool::Entry& sName);

			/// Read the model from a .x file
			void Read(const WtfEngine::String& sFile);

			// data manager interface
			static WtfEngine::IDataObject * Load(const WtfEngine::StringPool::Entry& sName);
			DATA_LOADABLE(XModel, GC_ADDEDSIZE_AUTO(GraphicsData, XModel));
	};

	namespace Test {
		class Monkey : public XMesh 
		{
			public:
				Monkey();
		};
		
		class Text : public XMesh 
		{
			public:
				Text ();
		};

		class Cloister : public XMesh 
		{
			public:
				Cloister ();
		};

		class Textured : public XMesh 
		{
			public:
				Textured ();
		};
		
		class Wall : public XMesh
		{
			public:
				Wall();
		};
	};
};

#endif
